home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / opt / pinstaller / GLIInstallProfile.py < prev    next >
Text File  |  2006-02-15  |  67KB  |  1,681 lines

  1. """
  2. # Copyright 1999-2005 Gentoo Foundation
  3. # This source code is distributed under the terms of version 2 of the GNU
  4. # General Public License as published by the Free Software Foundation, a copy
  5. # of which can be found in the main directory of this project.
  6. Gentoo Linux Installer
  7.  
  8. $Id: GLIInstallProfile.py,v 1.84 2006/02/10 02:01:58 agaffney Exp $
  9.  
  10. The GLI module contains all classes used in the Gentoo Linux Installer (or GLI).
  11. The InstallProfile contains all information related to the new system to be
  12. installed.
  13.  
  14. PROCEDURE TO ADD NEW VARIABLES:
  15.     1. Add a handler to the list.  If the variable has children make sure you do it right.
  16.        Look at the existing structure to get an idea.
  17.     2. Create a section for the two or three functions.
  18.     3. Create the get_variable_name and set_variable_name functions.
  19.        Ensure the set function has correct error checking.
  20.     4. If a simple value, add to the list in the general serialize() function.
  21.        If more complex add a serialize_variable_name to the list of special cases.
  22.        Then add the serialize_variable_name function to the section for the variable.
  23. """
  24.  
  25. import string
  26. import xml.sax
  27. import os
  28. import GLIUtility
  29. import SimpleXMLParser
  30. import xml.dom.minidom
  31. import GLIStorageDevice
  32. from GLIException import *
  33.  
  34. ##
  35. # This class contains all the settings used during the install
  36. class InstallProfile:
  37.     """
  38.     An object representation of a profile.
  39.     InstallProfile is an object representation of a parsed installation
  40.     profile file.
  41.     """
  42.  
  43.     ##
  44.     # Initializes all variables to default values and adds handlers.
  45.     def __init__(self):
  46.  
  47.         # Configuration information - profile data
  48.         # All variables must be declared here with a default value to ensure
  49.         # the XML will correctly serialize.
  50.         self._cron_daemon_pkg = ""
  51.         self._logging_daemon_pkg = ""
  52.         self._boot_device = ""
  53.         self._boot_loader_mbr = True
  54.         self._boot_loader_pkg = "grub"
  55.         self._kernel_modules = []
  56.         self._kernel_config_uri = ""
  57.         self._kernel_build_method = "genkernel"
  58.         self._bootloader_kernel_args = "acpi=force dopcmcia vga=788 nodhcp"
  59.         self._kernel_initrd = True
  60.         self._kernel_bootsplash = False
  61.         self._kernel_source_pkg = "livecd-kernel"
  62.         self._users = []
  63.         self._root_pass_hash = ""
  64.         self._time_zone = "UTC"
  65.         self._stage_tarball_uri = ""
  66.         self._install_stage = 3
  67.         self._portage_tree_sync_type = "snapshot"
  68.         self._portage_tree_snapshot_uri = ""
  69.         self._domainname = "localdomain"
  70.         self._hostname = "pentoo"
  71.         self._http_proxy = ""
  72.         self._ftp_proxy = ""
  73.         self._rsync_proxy = ""
  74.         self._nisdomainname = ""
  75.         self._partition_tables = {}
  76.         self._network_mounts = []
  77.         self._temp_partition_table = {} # temp
  78.         self._network_interfaces = {}
  79.         self._make_conf = {}
  80. #        self._rc_conf = {}
  81.         self._install_rp_pppoe = False
  82.         self._filesystem_tools = ()
  83.         self._install_pcmcia_cs = False
  84.         self._dns_servers = ()
  85.         self._default_gateway = ()
  86.         self._install_packages = ()
  87.         self._services = ()
  88.         self._mta_pkg = ""
  89.         self._grp_install = False
  90.         self._post_install_script_uri = ""
  91.         self._etc_files = {}
  92.         self._temp_etc_file = {}
  93.         self._dynamic_stage3 = False
  94.         self._install_distcc = False
  95.         self.xmldoc = ""
  96.  
  97.         # Parser handler calls.  For each XML attribute and children of that attribute, a handler is needed.
  98.         self._parser = SimpleXMLParser.SimpleXMLParser()
  99.         self._parser.addHandler('gli-profile/bootloader', self.set_boot_loader_pkg)
  100.         self._parser.addHandler('gli-profile/boot-device', self.set_boot_device)
  101.         self._parser.addHandler('gli-profile/bootloader-kernel-args', self.set_bootloader_kernel_args)
  102.         self._parser.addHandler('gli-profile/bootloader-mbr', self.set_boot_loader_mbr)
  103.         self._parser.addHandler('gli-profile/cron-daemon', self.set_cron_daemon_pkg)
  104.         self._parser.addHandler('gli-profile/default-gateway', self.set_default_gateway)
  105.         self._parser.addHandler('gli-profile/dns-servers', self.set_dns_servers)
  106.         self._parser.addHandler('gli-profile/domainname', self.set_domainname)
  107.         self._parser.addHandler('gli-profile/dynamic-stage3', self.set_dynamic_stage3)
  108.         self._parser.addHandler('gli-profile/etc-files/file', self.add_etc_files_file, call_on_null=True)
  109.         self._parser.addHandler('gli-profile/etc-files/file/entry', self.add_etc_files_file_entry, call_on_null=True)
  110.         self._parser.addHandler('gli-profile/ftp-proxy', self.set_ftp_proxy)
  111.         self._parser.addHandler('gli-profile/grp-install', self.set_grp_install)
  112.         self._parser.addHandler('gli-profile/hostname', self.set_hostname)
  113.         self._parser.addHandler('gli-profile/http-proxy', self.set_http_proxy)
  114.         self._parser.addHandler('gli-profile/install-distcc', self.set_install_distcc)
  115.         self._parser.addHandler('gli-profile/install-packages', self.set_install_packages)
  116.         self._parser.addHandler('gli-profile/install-pcmcia-cs', self.set_install_pcmcia_cs)
  117.         self._parser.addHandler('gli-profile/install-rp-pppoe', self.set_install_rp_pppoe)
  118.         self._parser.addHandler('gli-profile/install-stage', self.set_install_stage)
  119.         self._parser.addHandler('gli-profile/kernel-bootsplash', self.set_kernel_bootsplash)
  120.         self._parser.addHandler('gli-profile/kernel-build-method', self.set_kernel_build_method)
  121.         self._parser.addHandler('gli-profile/kernel-config', self.set_kernel_config_uri)
  122.         self._parser.addHandler('gli-profile/kernel-initrd', self.set_kernel_initrd)
  123.         self._parser.addHandler('gli-profile/kernel-modules/module', self.add_kernel_module)
  124.         self._parser.addHandler('gli-profile/kernel-source', self.set_kernel_source_pkg)
  125.         self._parser.addHandler('gli-profile/logging-daemon', self.set_logging_daemon_pkg)
  126. #        self._parser.addHandler('gli-profile/make-conf/variable', self.make_conf_add_var)
  127.         self._parser.addHandler('gli-profile/mta', self.set_mta_pkg)
  128.         self._parser.addHandler('gli-profile/network-interfaces/device', self.add_network_interface)
  129.         self._parser.addHandler('gli-profile/network-mounts/netmount', self.add_netmount, call_on_null=True)
  130.         self._parser.addHandler('gli-profile/nisdomainname', self.set_nisdomainname)
  131.         self._parser.addHandler('gli-profile/partitions/device', self.add_partitions_device, call_on_null=True)
  132.         self._parser.addHandler('gli-profile/partitions/device/partition', self.add_partitions_device_partition, call_on_null=True)
  133.         self._parser.addHandler('gli-profile/portage-snapshot', self.set_portage_tree_snapshot_uri)
  134.         self._parser.addHandler('gli-profile/portage-tree-sync', self.set_portage_tree_sync_type)
  135.         self._parser.addHandler('gli-profile/post-install-script-uri', self.set_post_install_script_uri)
  136. #        self._parser.addHandler('gli-profile/rc-conf/variable', self.rc_conf_add_var)
  137.         self._parser.addHandler('gli-profile/root-pass-hash', self.set_root_pass_hash)
  138.         self._parser.addHandler('gli-profile/rsync-proxy', self.set_rsync_proxy)
  139.         self._parser.addHandler('gli-profile/services', self.set_services)
  140.         self._parser.addHandler('gli-profile/stage-tarball', self.set_stage_tarball_uri)
  141.         self._parser.addHandler('gli-profile/time-zone', self.set_time_zone)
  142.         self._parser.addHandler('gli-profile/users/user', self.add_user)
  143.         
  144.     ##
  145.     # Parses the given filename populating the client_configuration.
  146.     # @param filename Parameter description
  147.     def parse(self, filename):
  148.         self._parser.parse(filename)
  149.  
  150.     ##
  151.     # This method serializes the configuration data and output a nice XML document.
  152.     # NOTE: this method currently does not serialize: _partition_tables or _kernel_modules
  153.     def serialize(self):
  154.         xmltab = { 'boot-device': self.get_boot_device,    
  155.               'bootloader':                self.get_boot_loader_pkg,
  156.                     'bootloader-mbr':            self.get_boot_loader_mbr,
  157.                     'bootloader-kernel-args':    self.get_bootloader_kernel_args,
  158.                     'cron-daemon':                self.get_cron_daemon_pkg,
  159.                     'domainname':                self.get_domainname,
  160.                     'dynamic-stage3':            self.get_dynamic_stage3,
  161.                     'ftp-proxy':                self.get_ftp_proxy,
  162.                     'grp-install':                self.get_grp_install,
  163.                     'hostname':                    self.get_hostname,
  164.                     'http-proxy':                self.get_http_proxy,
  165.                     'install-distcc':            self.get_install_distcc,
  166.                     'install-pcmcia-cs':        self.get_install_pcmcia_cs,
  167.                     'install-rp-pppoe':            self.get_install_rp_pppoe,
  168.                     'install-stage':            self.get_install_stage,
  169.                     'kernel-bootsplash':        self.get_kernel_bootsplash,
  170.                     'kernel-build-method':            self.get_kernel_build_method,
  171.                     'kernel-config':            self.get_kernel_config_uri,
  172.                     'kernel-initrd':            self.get_kernel_initrd,
  173.                     'kernel-source':            self.get_kernel_source_pkg,
  174.                     'logging-daemon':            self.get_logging_daemon_pkg,
  175.                     'mta':                        self.get_mta_pkg,
  176.                     'nisdomainname':            self.get_nisdomainname,
  177.                     'portage-snapshot':            self.get_portage_tree_snapshot_uri,
  178.                     'portage-tree-sync':        self.get_portage_tree_sync_type,
  179.                     'post-install-script-uri':    self.get_post_install_script_uri,
  180.                     'root-pass-hash':            self.get_root_pass_hash,
  181.                     'rsync-proxy':                self.get_rsync_proxy,
  182.                     'stage-tarball':            self.get_stage_tarball_uri,
  183.                     'time-zone':                self.get_time_zone,
  184.         }
  185.         self.xmldoc = "<?xml version=\"1.0\"?>"
  186.         self.xmldoc += "<gli-profile>"
  187.  
  188.         # Normal cases
  189.         for key in xmltab.keys():
  190.             self.xmldoc += "<%s>%s</%s>" % (key, xmltab[key](), key)
  191.  
  192.         # Special cases
  193.         self.serialize_default_gateway()
  194.         self.serialize_dns_servers()
  195.         self.serialize_install_packages()
  196.         self.serialize_kernel_modules()
  197. #        self.serialize_make_conf()
  198.         self.serialize_network_interfaces()
  199.         self.serialize_network_mounts()
  200.         self.serialize_partition_tables()
  201. #        self.serialize_rc_conf()
  202.         self.serialize_services()
  203.         self.serialize_users()
  204.         self.serialize_etc_files()
  205.  
  206.         self.xmldoc += "</gli-profile>"
  207.  
  208.         dom = xml.dom.minidom.parseString(self.xmldoc)
  209.         return dom.toprettyxml()
  210.     
  211.     ############################################################################
  212.     ############################################################################
  213.     #### Boot Device Selection
  214.     
  215.     ##
  216.     # boot_device is a string to decide which device to install the bootloader to
  217.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  218.     # @param boot_loader_pkg   boot device with full /dev
  219.     # @param xml_attr         not used here
  220.     
  221.     def set_boot_device(self, xml_path, boot_device, xml_attr):
  222.         #check data type
  223.         if type(boot_device) != str:
  224.             raise GLIException("BootDevice", 'fatal', 'set_boot_device',  "Input must be type 'string'!")
  225.         self._boot_device = boot_device
  226.     
  227.     ##
  228.     # returns boot_device
  229.     def get_boot_device(self):
  230.         return self._boot_device
  231.     
  232.     ############################################################################
  233.     #### Bootloader Package Selection
  234.  
  235.     ##
  236.     # boot_loader_pkg is a string to decide which boot loader to install. (ie. 'grub')
  237.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  238.     # @param boot_loader_pkg   bootloader package name (like grub or lilo)
  239.     # @param xml_attr         not used here
  240.     def set_boot_loader_pkg(self, xml_path, boot_loader_pkg, xml_attr):
  241.         # Check data type
  242.         if type(boot_loader_pkg) != str:
  243.             raise GLIException("BootLoaderPKG", 'fatal', 'set_boot_loader_pkg',  "Input must be type 'string'!")
  244.         self._boot_loader_pkg = boot_loader_pkg
  245.  
  246.     ##
  247.     # returns boot_loader_pkg
  248.     def get_boot_loader_pkg(self):
  249.         return self._boot_loader_pkg
  250.     
  251.     ############################################################################
  252.     #### Bootloader Kernel Arguments
  253.     
  254.     ##
  255.     # FIXME: agaffney, what error checking needs to be done here?
  256.     # kernel_args are the arguments to pass the kernel at boot from the bootloader.
  257.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  258.     # @param bootloader_kernel_args   FIXME no idea.
  259.     # @param xml_attr         not used here.
  260.     def set_bootloader_kernel_args(self, xml_path, bootloader_kernel_args, xml_attr):
  261.         self._bootloader_kernel_args = bootloader_kernel_args
  262.  
  263.     ##
  264.     # Returns kernel arguments
  265.     def get_bootloader_kernel_args(self):
  266.         return self._bootloader_kernel_args
  267.         
  268.     ############################################################################
  269.     #### Bootloader Installation to MBR
  270.  
  271.     ##
  272.     # boot_loader_mbr is a bool. True installs boot loader to MBR.  
  273.     # False installs boot loader to the boot or root partition.
  274.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  275.     # @param boot_loader_mbr  
  276.     # @param xml_attr Parameter description
  277.     def set_boot_loader_mbr(self, xml_path, boot_loader_mbr, xml_attr):
  278.         # Check data type
  279.         if type(boot_loader_mbr) != bool:
  280.             if type(boot_loader_mbr) == str:
  281.                 boot_loader_mbr = GLIUtility.strtobool(boot_loader_mbr)
  282.             else:
  283.                 raise GLIException("BootLoaderMBRError", 'fatal', 'set_boot_loader_mbr',  "Input must be type 'bool'!")
  284.         self._boot_loader_mbr = boot_loader_mbr
  285.  
  286.     ##
  287.     # returns boot_loader_mbr
  288.     def get_boot_loader_mbr(self):
  289.         return self._boot_loader_mbr    
  290.  
  291.     ############################################################################
  292.     #### Cron Daemon Package
  293.  
  294.     ##
  295.     # cron_daemon_pkg is a string to determine which cron daemon to install and configure (ie. 'vixie-cron')
  296.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  297.     # @param cron_daemon_pkg package name
  298.     # @param xml_attr Not used here.
  299.     def set_cron_daemon_pkg(self, xml_path, cron_daemon_pkg, xml_attr):
  300.         # Check data type
  301.         if type(cron_daemon_pkg) != str:
  302.             raise GLIException("CronDaemonPKGError", 'fatal', 'set_cron_daemon_pkg',  "Input must be type 'string'!")
  303.         self._cron_daemon_pkg = cron_daemon_pkg
  304.  
  305.     ##
  306.     # Returns the cron daemon pkg name
  307.     def get_cron_daemon_pkg(self):
  308.         return self._cron_daemon_pkg
  309.  
  310.     ############################################################################
  311.     #### Network Gateway 
  312.  
  313.     ##
  314.     # Set the default gateway for the post-installed system.
  315.     # The format of the input is: <default-gateway interface="interface name">ip of gateway</default-gateway>
  316.     # It saves this information in the following format: (<interface>, <ip of gateway>)
  317.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  318.     # @param gateway          gateway ip address
  319.     # @param xml_attr Parameter description
  320.     def set_default_gateway(self, xml_path, gateway, xml_attr):
  321.         if not GLIUtility.is_realstring(gateway):
  322.             raise GLIException('DefaultGatewayError', 'fatal', 'set_default_gateway', "The gateway must be a non-empty string!")
  323.         if not 'interface' in xml_attr.keys():
  324.             raise GLIException('DefaultGatewayError', 'fatal', 'set_default_gateway', 'No interface information specified!')
  325.  
  326.         interface = str(xml_attr['interface'])
  327.  
  328.         if not GLIUtility.is_eth_device(interface):
  329.             raise GLIException('DefaultGatewayError', 'fatal', 'set_default_gateway', "Invalid device!")
  330.         if not GLIUtility.is_ip(gateway):
  331.             raise GLIException("DefaultGateway", 'fatal', 'set_default_gateway',  "The IP Provided is not valid!")
  332.         self._default_gateway = (interface, gateway)
  333.  
  334.     ##
  335.     # Returns the default gateway
  336.     def get_default_gateway(self):
  337.         return self._default_gateway
  338.  
  339.     ##
  340.     # Serializes default_gateway
  341.     def serialize_default_gateway(self):
  342.         if self.get_default_gateway() != ():
  343.             gw = self.get_default_gateway()
  344.             self.xmldoc += "<default-gateway interface=\"%s\">%s</default-gateway>" % (gw[0], gw[1])
  345.  
  346.  
  347.     ############################################################################
  348.     #### DNS Servers
  349.  
  350.     ##
  351.     # Set the DNS servers for the post-installed system.
  352.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  353.     # @param dns_servers     a tuple or space-separated list of dns servers
  354.     # @param xml_attr Parameter description
  355.     def set_dns_servers(self, xml_path, dns_servers, xml_attr):
  356.         if type(dns_servers) == tuple:
  357.             dns_servers = dns_servers[0:3]
  358.         elif type(dns_servers) == str:
  359.             dns_servers = string.split(dns_servers)
  360.         else:
  361.             raise GLIException("DnsServersError", 'fatal', 'set_dns_servers',  "Invalid input!")
  362.  
  363.         for server in dns_servers:
  364.             if not GLIUtility.is_ip(server):
  365.                 raise GLIException("DnsServersError", 'fatal', 'set_dns_servers',  server + " must be a valid IP address!")
  366.  
  367.         self._dns_servers = dns_servers
  368.  
  369.     ##
  370.     # This returns a tuple of the form: (<nameserver 1>, <nameserver 2>, <nameserver 3>)
  371.     def get_dns_servers(self):
  372.         return self._dns_servers
  373.         
  374.     ##
  375.     # Serializes DNS Servers
  376.     def serialize_dns_servers(self):
  377.         if self.get_dns_servers() != ():
  378.             self.xmldoc += "<dns-servers>"
  379.             self.xmldoc += string.join(self.get_dns_servers(), ' ')
  380.             self.xmldoc += "</dns-servers>"
  381.  
  382. ############################################################################
  383.     #### Domainname
  384.  
  385.     ##
  386.     # domainname is a string containing the domainname for the new system. (ie. 'mydomain.com'; NOT FQDN)
  387.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  388.     # @param domainname     string of the domain name
  389.     # @param xml_attr         not used here
  390.     def set_domainname(self, xml_path, domainname, xml_attr):
  391.         # Check type
  392.         if type(domainname) != str:
  393.             raise GLIException("DomainnameError", 'fatal', 'set_domainname',  "Must be a string!")
  394.         self._domainname = domainname
  395.  
  396.     ##
  397.     # Returns domainname
  398.     def get_domainname(self):
  399.         return self._domainname
  400.         
  401.     ###################################################################
  402.     ##
  403.     # Set whether or not to build the stage3 from the LiveCD
  404.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  405.     # @param dynamic_stage3    True/False     
  406.     # @param xml_attr         not used here
  407.     def set_dynamic_stage3(self, xml_path, dynamic_stage3, xml_attr):
  408.         if type(dynamic_stage3) != bool:
  409.             if type(dynamic_stage3) == str:
  410.                 self._dynamic_stage3 = GLIUtility.strtobool(dynamic_stage3)
  411.             else:
  412.                 raise GLIException("DynamicStage3", 'fatal', 'set_dynamic_stage3',  "Input must be type 'bool'!")
  413.         else:
  414.             self._dynamic_stage3 = dynamic_stage3
  415.  
  416.     ##
  417.     # Returns whether or not to build the stage3 from the LiveCD
  418.     def get_dynamic_stage3(self):
  419.         return self._dynamic_stage3
  420.  
  421.     ##
  422.     # Used internally for XML parsing...adds an entry to a file
  423.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  424.     # @param value uhh...the value
  425.     # @param attr used for XML parsing
  426.     def add_etc_files_file_entry(self, xml_path, value, attr):
  427.         if 'name' in attr.getNames():
  428.             if not self._temp_etc_file:
  429.                 self._temp_etc_file = {}
  430.             self._temp_etc_file[attr['name']] = value
  431.         else:
  432.             if not self._temp_etc_file:
  433.                 self._temp_etc_file = []
  434.             self._temp_etc_file.append(value)
  435.  
  436.     ##
  437.     # Used internally for XML parsing...adds a file
  438.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  439.     # @param unused this should be obvious
  440.     # @param attr used for XML parsing
  441.     def add_etc_files_file(self, xml_path, unused, attr):
  442.         self._etc_files[attr['name']] = self._temp_etc_file
  443.         self._temp_etc_file = None
  444.  
  445.     ##
  446.     # Returns etc_files structure
  447.     def get_etc_files(self):
  448.         return self._etc_files
  449.  
  450.     ##
  451.     # Replaces etc_files structure with one passed in
  452.     # @param etc_files new etc_files structure
  453.     def set_etc_files(self, etc_files):
  454.         self._etc_files = etc_files
  455.  
  456.     ##
  457.     # Serializes the etc_files structure
  458.     def serialize_etc_files(self):
  459.         self.xmldoc += "<etc-files>"
  460.         for etc_file in self._etc_files:
  461.             self.xmldoc += "<file name=\"%s\">" % etc_file
  462.             for entry in self._etc_files[etc_file]:
  463.                 self.xmldoc += "<entry"
  464.                 if isinstance(self._etc_files[etc_file], dict):
  465.                     self.xmldoc += " name=\"%s\">%s" % (entry, self._etc_files[etc_file][entry])
  466.                 else:
  467.                     self.xmldoc += ">%s" % entry
  468.                 self.xmldoc += "</entry>"
  469.             self.xmldoc += "</file>"
  470.         self.xmldoc += "</etc-files>"
  471.  
  472.         
  473.     ############################################################################
  474.     #### FTP Proxy
  475.  
  476.     ##
  477.     # FTP proxy is a uri containing a proxy if needed for ftp traffic. (ie. 'ftp://myhost.mydomain:myport')
  478.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  479.     # @param ftp_proxy     ftp proxy address
  480.     # @param xml_attr  not used here
  481.     def set_ftp_proxy(self, xml_path, ftp_proxy, xml_attr):
  482.         # Check type
  483.         if ftp_proxy and not GLIUtility.is_uri(ftp_proxy):
  484.             raise GLIException("FTPProxyError", 'fatal', 'set_ftp_proxy',  "Must be a uri!")
  485.         self._ftp_proxy = ftp_proxy
  486.  
  487.     ##
  488.     # Returns FTP proxy
  489.     def get_ftp_proxy(self):
  490.         return self._ftp_proxy
  491.     
  492.     ############################################################################
  493.     #### GRP Install
  494.     
  495.     ##
  496.     # grp_install is a bool. True installs GRP.  False doesn't.
  497.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  498.     # @param grp_install  boolean
  499.     # @param xml_attr Parameter description
  500.     def set_grp_install(self, xml_path, grp_install, xml_attr):
  501.         # Check data type
  502.         if type(grp_install) != bool:
  503.             if type(grp_install) == str:
  504.                 grp_install = GLIUtility.strtobool(grp_install)
  505.             else:
  506.                 raise GLIException("GRPInstall", 'fatal', 'set_grp_install',  "Input must be type 'bool'!")
  507.         self._grp_install = grp_install
  508.         
  509.     ##
  510.     # returns grp_install
  511.     def get_grp_install(self):
  512.         return self._grp_install
  513.  
  514.     ############################################################################
  515.     #### Hostname
  516.  
  517.     ##
  518.     # Hostname is a string containing the hostname for the new system. (ie. 'myhost'; NOT 'myhost.mydomain.com')
  519.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  520.     # @param hostname         string of hostname
  521.     # @param xml_attr         not used here.
  522.     def set_hostname(self, xml_path, hostname, xml_attr):
  523.         # Check type
  524.         if type(hostname) != str:
  525.             raise GLIException("HostnameError", 'fatal', 'set_hostname',  "Must be a string!")
  526.         self._hostname = hostname
  527.  
  528.     ##
  529.     # Returns hostname
  530.     def get_hostname(self):
  531.         return self._hostname
  532.  
  533.     ############################################################################
  534.     #### HTTP Proxy
  535.  
  536.     ##
  537.     # HTTP proxy is a URI containing a proxy if needed for http traffic. (ie. 'http://myhost.mydomain:myport')
  538.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  539.     # @param http_proxy     http proxy address
  540.     # @param xml_attr  not used here
  541.     def set_http_proxy(self, xml_path, http_proxy, xml_attr):
  542.         # Check type
  543.         if http_proxy and not GLIUtility.is_uri(http_proxy):
  544.             raise GLIException("HTTPProxyError", 'fatal', 'set_http_proxy',  "Must be a uri!")
  545.         self._http_proxy = http_proxy
  546.  
  547.     ##
  548.     # Returns HTTP proxy
  549.     def get_http_proxy(self):
  550.         return self._http_proxy
  551.  
  552.     ############################################################################
  553.     #### Install Distcc
  554.     
  555.     ##
  556.     # This tells the installer whether or not to install the distcc package
  557.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  558.     # @param install_distcc     boolean
  559.     # @param xml_attr Parameter description
  560.     def set_install_distcc(self, xml_path, install_distcc, xml_attr):
  561.         if type(install_distcc) != bool:
  562.             if type(install_distcc) == str:
  563.                 install_distcc = GLIUtility.strtobool(install_distcc)
  564.             else:
  565.                 raise GLIException("InstallDistcc", 'fatal', 'set_install_distcc',  "Input must be type 'bool'!")
  566.  
  567.         self._install_distcc = install_distcc
  568.  
  569.     ##
  570.     # Returns the boolean _install_distcc
  571.     def get_install_distcc(self):
  572.         return self._install_distcc
  573.  
  574.     ############################################################################
  575.     #### Install Package List
  576.     ##
  577.     # Returns the list of packages to include in the Extra Packages screens.
  578.     def get_install_package_list(self):
  579.         install_package_list = { 
  580.             'Desktop': ("Popular Desktop Applications",
  581.                 {"bittorrent": "tool for distributing files via a distributed network of nodes",
  582.                 "evolution": "A GNOME groupware application, a Microsoft Outlook workalike",
  583.                 "gaim": "GTK Instant Messenger client",
  584.                 "gftp": "Gnome based FTP Client",
  585.                 "gimp": "GNU Image Manipulation Program",
  586.                 "inkscape": "A SVG based generic vector-drawing program",
  587.                 "koffice": "An integrated office suite for KDE, the K Desktop Environment",
  588.                 "mozilla": "The Mozilla Web Browser",
  589.                 "mozilla-firefox": "The Mozilla Firefox Web Browser",
  590.                 "mozilla-thunderbird": "Thunderbird Mail Client",
  591.                 "mplayer": "Media Player for Linux",
  592.                 "openoffice": "OpenOffice.org, a full office productivity suite.",
  593.                 "openoffice-bin": "Same as OpenOffice but a binary package (no compiling!)",
  594.                 "realplayer": "Real Media Player",
  595.                 "rhythmbox": "Music management and playback software for GNOME",
  596.                 "vlc": "VLC media player - Video player and streamer",
  597.                 "xchat": "Graphical IRC Client",
  598.                 "xine-ui": "Xine movie player",
  599.                 "xmms": "X MultiMedia System"  }),
  600.             'Servers': ("Applications often found on servers.",
  601.                 {"apache": "Apache Web Server",
  602.                 "cups": "The Common Unix Printing System",
  603.                 "exim": "A highly configurable, drop-in replacement for sendmail",
  604.                 "iptables": "Linux kernel (2.4+) firewall, NAT and packet mangling tools",
  605.                 "mod_php": "Apache module for PHP",
  606.                 "mysql": "A fast, multi-threaded, multi-user SQL database server",
  607.                 "postfix": "A fast and secure drop-in replacement for sendmail",
  608.                 "postgresql": "sophisticated Object-Relational DBMS",
  609.                 "proftpd": "ProFTP Server",
  610.                 "samba": "SAMBA client/server programs for UNIX",
  611.                 "sendmail": "Widely-used Mail Transport Agent (MTA)",
  612.                 "traceroute": "Utility to trace the route of IP packets"  }),
  613.             'X11': ("Window managers and X selection.", 
  614.                 {"xorg-x11": "An X11 implementation maintained by the X.Org Foundation",
  615.                 "gnome": "The Gnome Desktop Environment",
  616.                 "kde": "The K Desktop Environment",
  617.                 "blackbox": "A small, fast, full-featured window manager for X",
  618.                 "enlightenment": "Enlightenment Window Manager",
  619.                 "fluxbox": "Fluxbox is an X11 window manager featuring tabs and an iconbar",
  620.                 "xfce4": "XFCE Desktop Environment"  }),
  621.             'Misc': ("Miscellaneous Applications you may want.",
  622.                 {"emacs": "An incredibly powerful, extensible text editor",
  623.                 "ethereal": "A commercial-quality network protocol analyzer",
  624.                 "gkrellm": "Single process stack of various system monitors",
  625.                 "gvim": "GUI version of the Vim text editor",
  626.                 "keychain": "ssh-agent manager",
  627.                 "logrotate": "Rotates, compresses, and mails system logs",
  628.                 "ntp": "Network Time Protocol suite/programs",
  629.                 "rdesktop": "A Remote Desktop Protocol Client",
  630.                 "slocate": "Secure way to index and quickly search for files on your system",
  631.                 "ufed": "Gentoo Linux USE flags editor",
  632.                 "vim": "Vim, an improved vi-style text editor" }),
  633.             'Recommended': ("Applications recommended by the GLI Team.",
  634.                 {"anjuta": "A versatile IDE for GNOME",
  635.                 "chkrootkit": "a tool to locally check for signs of a rootkit",
  636.                 "crack-attack": "Addictive OpenGL-based block game",
  637.                 "gnupg": "The GNU Privacy Guard, a GPL pgp replacement",
  638.                 "net-snmp": "Software for generating and retrieving SNMP data",
  639.                 "netcat": "the network swiss army knife",
  640.                 "nmap": "A utility for network exploration or security auditing",
  641.                 "screen": "full-screen window manager that multiplexes between several processes",
  642.                 "xpdf": "An X Viewer for PDF Files" })
  643.         }
  644.         return install_package_list
  645.  
  646.     ############################################################################
  647.     #### Install Packages
  648.  
  649.     ##
  650.     # Sets up the list of packages to be installed for the new system.
  651.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  652.     # @param install_packages The space-separated list of packages to install.
  653.     # @param xml_attr Parameter description
  654.     def set_install_packages(self, xml_path, install_packages, xml_attr):
  655.         if type(install_packages) == str:
  656.             install_packages = string.split(install_packages)
  657.         else:
  658.             raise GLIException("InstallPackagesError", 'fatal', 'set_install_packages',  "Invalid input!")
  659.  
  660.         for install_package in install_packages:
  661.             if not GLIUtility.is_realstring(install_package):
  662.                 raise GLIException("InstallPackagesError", 'fatal', 'set_install_packages',  install_package + " must be a valid string!")
  663.         self._install_packages = install_packages
  664.  
  665.     ##
  666.     # This returns a list of the packages
  667.     def get_install_packages(self):
  668.         return self._install_packages
  669.         
  670.     ##
  671.     # Serializes install_packages
  672.     def serialize_install_packages(self):
  673.         if self.get_install_packages() != ():
  674.             self.xmldoc += "<install-packages>"
  675.             self.xmldoc += string.join(self.get_install_packages(), ' ')
  676.             self.xmldoc += "</install-packages>"
  677.  
  678.     ############################################################################
  679.     #### PCMCIA-CS 
  680.  
  681.     ##
  682.     # This tells the installer whether or not to install the pcmcia_cs package
  683.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  684.     # @param install_pcmcia     boolean
  685.     # @param xml_attr Parameter description
  686.     def set_install_pcmcia_cs(self, xml_path, install_pcmcia, xml_attr):
  687.         if type(install_pcmcia) != bool:
  688.             if type(install_pcmcia) == str:
  689.                 install_pcmcia = GLIUtility.strtobool(install_pcmcia)
  690.             else:
  691.                 raise GLIException("InstallPcmciaCS", 'fatal', 'set_install_pcmcia_cs',  "Input must be type 'bool'!")
  692.  
  693.         self._install_pcmcia_cs = install_pcmcia
  694.  
  695.     ##
  696.     # Returns the boolean _install_pcmcia_cs
  697.     def get_install_pcmcia_cs(self):
  698.         return self._install_pcmcia_cs
  699.  
  700.     ############################################################################
  701.     #### RP-PPPoE Installation
  702.  
  703.     ##
  704.     # Tell the installer whether or not to install the rp-pppoe package
  705.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  706.     # @param install_rp_pppoe        boolean
  707.     # @param xml_attr Parameter description
  708.     def set_install_rp_pppoe(self, xml_path, install_rp_pppoe, xml_attr):
  709.         if type(install_rp_pppoe) != bool:
  710.             if type(install_rp_pppoe) == str:
  711.                 install_rp_pppoe = GLIUtility.strtobool(install_rp_pppoe)
  712.             else:
  713.                 raise GLIException("InstallRP_PPPOE", 'fatal', 'set_install_rp_pppoe',  "Invalid input!")
  714.  
  715.         self._install_rp_pppoe = install_rp_pppoe
  716.  
  717.     ##
  718.     # Return the boolean value of _install_rp_pppoe
  719.     def get_install_rp_pppoe(self):
  720.         return self._install_rp_pppoe
  721.  
  722.     ############################################################################
  723.     #### Install Stage
  724.  
  725.     ##
  726.     # install_stage is a integer to define which stage install to use.  Appropriate stages are 1-3.
  727.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  728.     # @param install_stage         install stage number
  729.     # @param xml_attr  not used here.
  730.     def set_install_stage(self, xml_path, install_stage, xml_attr):
  731.         # Check type
  732.         if type(install_stage) != int:
  733.             if type(install_stage) == str:
  734.                 install_stage = int(install_stage)
  735.             else:
  736.                 raise GLIException("InstallStageError", 'fatal', 'set_install_stage',  "Must be an integer!")
  737.         
  738.         # Check for stage bounds
  739.         if 0 < install_stage < 4:
  740.             self._install_stage = install_stage
  741.         else:
  742.             raise GLIException("InstallStageError", 'fatal', 'set_install_stage',  "install_stage must be 1-3!")
  743.  
  744.     ##
  745.     # Returns install_stage
  746.     def get_install_stage(self):
  747.         return self._install_stage
  748.  
  749.     ############################################################################
  750.     #### Kernel Bootsplash Option
  751.     
  752.     ##
  753.     # kernel_bootsplash is a bool to determine whether or not to install bootsplash into the kernel.  
  754.     # True builds in bootsplash support to the initrd.  
  755.     # WARNING: kernel_source_pkg must contain a kernel with bootsplash support or the bootsplash will not appear.  
  756.     # If you set this to true, it will build an initrd kernel even if you chose false for kernel_initrd!
  757.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  758.     # @param kernel_bootsplash         boolean
  759.     # @param xml_attr         not used here.
  760.     def set_kernel_bootsplash(self, xml_path, kernel_bootsplash, xml_attr):
  761.         # Check type
  762.         if type(kernel_bootsplash) != bool:
  763.             if type(kernel_bootsplash) == str:
  764.                     kernel_bootsplash = GLIUtility.strtobool(kernel_bootsplash)
  765.             else:
  766.                 raise GLIException("KernelBootsplashError", 'fatal', 'set_kernel_bootsplash',  "Must be a bool!")
  767.         
  768.         self._kernel_bootsplash = kernel_bootsplash
  769.  
  770.     ##
  771.     # Returns kernel_bootsplash
  772.     def get_kernel_bootsplash(self):
  773.         return self._kernel_bootsplash        
  774.     
  775.     ############################################################################
  776.     #### Kernel Build Method
  777.  
  778.     ##
  779.     # kernel_build_method is a string specifying what build method you wish to use for the kernel.
  780.     # Can also be a http:// or ftp:// path.
  781.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  782.     # @param kernel_build_method   URI of kernel .config file
  783.     # @param xml_attr      not used here.
  784.     def set_kernel_build_method(self, xml_path, kernel_build_method, xml_attr):
  785.         # Check type
  786.         if type(kernel_build_method) != str:
  787.             raise GLIException("KernelBuildMethodError", 'fatal', 'set_kernel_build_method',  "Must be a string!")
  788.  
  789.         self._kernel_build_method = kernel_build_method
  790.  
  791.     ##
  792.     # Returns kernel_build_method
  793.     def get_kernel_build_method(self):
  794.         return self._kernel_build_method
  795.         
  796.     ############################################################################
  797.     #### Kernel Configuration URI
  798.  
  799.     ##
  800.     # kernel_config_uri is a string that is the path to the kernel config file you wish to use.  
  801.     # Can also be a http:// or ftp:// path.
  802.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  803.     # @param kernel_config_uri   URI of kernel .config file
  804.     # @param xml_attr      not used here.
  805.     def set_kernel_config_uri(self, xml_path, kernel_config_uri, xml_attr):
  806.         # Check type
  807.         if type(kernel_config_uri) != str:
  808.             raise GLIException("KernelConfigURIError", 'fatal', 'set_kernel_config_uri',  "Must be a string!")
  809.  
  810.         # Check validity (now done in the FE)
  811.         #if not (kernel_config_uri):
  812.         #    raise GLIException("KernelConfigURIError", 'fatal', 'set_kernel_config_uri',  "Empty Kernel URI!")
  813.  
  814.         self._kernel_config_uri = kernel_config_uri
  815.  
  816.     ##
  817.     # Returns kernel_config_uri
  818.     def get_kernel_config_uri(self):
  819.         return self._kernel_config_uri
  820.  
  821.     ############################################################################
  822.     #### Kernel Initrd Option
  823.     
  824.     ##
  825.     # kernel_initrd is a bool to determine whether or not to build an initrd kernel.  False builds a non-initrd kernel. 
  826.     # (overwritten by kernel_bootsplash; needs genkernel non-initrd support not yet present)
  827.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  828.     # @param kernel_initrd         boolean
  829.     # @param xml_attr         not used here
  830.     def set_kernel_initrd(self, xml_path, kernel_initrd, xml_attr):
  831.         # Check type
  832.         if type(kernel_initrd) != bool:
  833.             if type(kernel_initrd) == str:
  834.                 kernel_initrd = GLIUtility.strtobool(kernel_initrd)
  835.             else:
  836.                 raise GLIException("KernelInitRDError", 'fatal', 'set_kernel_initrd',  "Must be a bool!")
  837.         
  838.         self._kernel_initrd = kernel_initrd
  839.  
  840.     ##
  841.     # Returns kernel_initrd
  842.     def get_kernel_initrd(self):
  843.         return self._kernel_initrd
  844.  
  845.     ############################################################################
  846.     #### Kernel Modules
  847.  
  848.     ##
  849.     # Add a kernel module to the list of kernel modules
  850.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  851.     # @param kernel_module         string of a module name
  852.     # @param xml_attr         not used here
  853.     def add_kernel_module(self, xml_path, kernel_module, xml_attr):
  854.         if type(kernel_module) != str:
  855.             raise GLIException("KernelModuleError", 'fatal', 'add_kernel_module',  "The kernel module must be a string!")
  856.         self._kernel_modules.append(kernel_module)
  857.         
  858.     ##
  859.     # "kernel_modules is a tuple of strings containing names of modules to automatically load at boot time. (ie. '( 'ide-scsi', )')"
  860.     # @param kernel_modules Parameter description
  861.     def set_kernel_modules(self, kernel_modules):
  862.         # Check type
  863.         if type(kernel_modules) != tuple:
  864.             raise GLIException("KernelModulesError", 'fatal', 'set_kernel_modules',  "Must be a tuple!")
  865.         
  866.         self._kernel_modules = []
  867.     
  868.         # Check tuple data type
  869.         for module in kernel_modules:
  870.             self._kernel_modules.append(module)
  871.  
  872.     ##
  873.     # Returns kernel_modules
  874.     def get_kernel_modules(self):
  875.         return self._kernel_modules
  876.         
  877.     ##
  878.     # Serializes kernel modules
  879.     def serialize_kernel_modules(self):
  880.         if self.get_kernel_modules() != []:
  881.             kernel_modules = self.get_kernel_modules()
  882.             self.xmldoc += "<kernel-modules>";
  883.             for module in kernel_modules:
  884.                 self.xmldoc += "<module>%s</module>" % module
  885.             self.xmldoc += "</kernel-modules>";
  886.  
  887.     ############################################################################
  888.     #### Kernel Sources
  889.     
  890.     ##
  891.     # kernel_source_pkg is a string to define which kernel source to use.  (ie. 'gentoo-sources')
  892.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  893.     # @param kernel_source_pkg   package name of the kernel sources to be emerged
  894.     # @param xml_attr     not used here.
  895.     def set_kernel_source_pkg(self, xml_path, kernel_source_pkg, xml_attr):
  896.         # Check type
  897.         if type(kernel_source_pkg) != str:
  898.             raise GLIException("KernelSourcePKGError", 'fatal', 'set_kernel_source_pkg',  "Must be a string!")
  899.         self._kernel_source_pkg = kernel_source_pkg
  900.  
  901.     ##
  902.     # Returns kernel_source_pkg
  903.     def get_kernel_source_pkg(self):
  904.         return self._kernel_source_pkg
  905.         
  906.     ############################################################################
  907.     #### Logging Daemon Package
  908.     
  909.     ##
  910.     # logging_daemon_pkg is a string to determine which logging daemon to install and configure (ie. 'sysklogd')
  911.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  912.     # @param logging_daemon_pkg package name of logging daemon to be emerged
  913.     # @param xml_attr Parameter description
  914.     def set_logging_daemon_pkg(self, xml_path, logging_daemon_pkg, xml_attr):
  915.         # Check data type
  916.         if type(logging_daemon_pkg) != str:
  917.             raise GLIException("LoggingDaemonPKGError", 'fatal', 'set_logging_daemon_pkg',  "Input must be type 'string'!")
  918.         self._logging_daemon_pkg = logging_daemon_pkg
  919.     
  920.     ##
  921.     # Returns logging daemon pkg name
  922.     def get_logging_daemon_pkg(self):
  923.         return self._logging_daemon_pkg
  924.  
  925.     ############################################################################
  926.     #### /etc/make.conf Configuration
  927.  
  928.     ##
  929.     # Adds a variable to the new system make.conf
  930.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  931.     # @param data         a string that is the value of the variable name.
  932.     # @param attr         an xml attribute that contains the name of the variable
  933.     #         OR attr is a variable name, like 'USE'. This makes it easier for front-end designers.
  934.     def make_conf_add_var(self, xml_path, data, attr):
  935.         if 'name' not in attr.keys():
  936.             raise GLIException("MakeConfError", 'fatal', 'make_conf_add_var',  "Every value needs to have a variable name!")
  937.  
  938.         varName = attr['name']
  939.         if not "make.conf" in self._etc_files:
  940.             self._etc_files['make.conf'] = {}
  941.         self._make_conf[str(varName)] = str(data)
  942.  
  943.     ##
  944.     # make_conf is a dictionary that will be set to _make_conf
  945.     # There is no checking that needs to be done, so please sure sure that the make_conf dictionary
  946.     # that is passed in is valid.
  947.     # @param make_conf         a dictionary that will be set to _make_conf
  948.     def set_make_conf(self, make_conf):
  949.         self._etc_files['make.conf'] = make_conf
  950.  
  951.     ##
  952.     # Return a dictionary of the make.conf
  953.     def get_make_conf(self):
  954.         if "make.conf" in self._etc_files:
  955.             return self._etc_files['make.conf']
  956.         else:
  957.             return {}
  958.         
  959.     ##
  960.     # Serializes make.conf (no longer used)
  961.     def serialize_make_conf(self):
  962.         if self.get_make_conf() != {}:
  963.             self.xmldoc += "<make-conf>"
  964.  
  965.             # keys should always be in the same order!
  966.             make_conf = self.get_make_conf()
  967.             make_conf_keys = make_conf.keys()
  968.             make_conf_keys.sort()
  969.  
  970.             for var in make_conf_keys:
  971.                 self.xmldoc += "<variable name=\"%s\">%s</variable>" % (var, make_conf[var])
  972.  
  973.             self.xmldoc += "</make-conf>"
  974.  
  975.     ############################################################################
  976.     #### MTA Selection
  977.  
  978.     ##
  979.     # Sets the intended MTA package
  980.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  981.     # @param mta     package name of mta
  982.     # @param xml_attr Parameter description
  983.     def set_mta_pkg(self, xml_path, mta_pkg, xml_attr):
  984.         if type(mta_pkg) != str:
  985.             raise GLIException("MTAError", 'fatal', 'set_mta_pkg',  "The MTA must be a string!")
  986.         self._mta_pkg = mta_pkg
  987.  
  988.     ##
  989.     # returns the MTA
  990.     def get_mta_pkg(self):
  991.         return self._mta_pkg
  992.     
  993.     ############################################################################
  994.     #### Network Interface Selection
  995.  
  996.     ##
  997.     # This adds an ethernet device to the _network_interfaces dictionary.
  998.     #    The format of this dictionary is:
  999.     #    { <eth_device> : (options tuple), ... }
  1000.     #
  1001.     #    eth_device can be a valid ethernet device eth0, eth1, wlan*... OR 
  1002.     #    it can be a valid MAC address.
  1003.     #
  1004.     #    The format of the options tuple is for a static IP:
  1005.     #    ( <ip address>, <broadcast>, <netmask> )
  1006.     #
  1007.     #    For DHCP, the format of the options tuple is:
  1008.     #    ( 'dhcp', <dhcp options>, None )
  1009.     #
  1010.     #    We keep the None as a placeholder, to not break anything that uses this
  1011.     #    in other parts of the installer.
  1012.     #    
  1013.     #    Aliases are no longer needed in the tuple because they can be treated like
  1014.     #    an individual interface. GLIUtility.is_eth_device will recognize
  1015.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1016.     # @param device      the type and name of the device
  1017.     # @param attr         should be dhcp or a tuple of the ip addresses.
  1018.     def add_network_interface(self, xml_path, device, attr):
  1019.         options = None
  1020.         ip = broadcast = netmask = dhcp_options = None
  1021.         dhcp = True
  1022.  
  1023.         if type(device) != str:
  1024.             raise GLIException("NetworkInterfacesError", 'fatal', 'add_network_interface',  "Invalid or unimplimented device type (" + device + ")!")
  1025.     
  1026.         if not GLIUtility.is_eth_device(device):
  1027.             device = GLIUtility.format_mac(device)
  1028.             if not GLIUtility.is_mac(device):
  1029.                 raise GLIException("NetworkInterfacesError", 'fatal', 'add_network_interface',  "Invalid or unimplimented device type (" + device + ")!")
  1030.  
  1031.         if type(attr) == tuple:
  1032.             ip = attr[0]
  1033.             dhcp_options = broadcast = attr[1]
  1034.             netmask = attr[2]
  1035.             if ip != 'dhcp':
  1036.                 dhcp = False
  1037.         else:
  1038.             for attrName in attr.keys():
  1039.                 if attrName == 'ip':
  1040.                     ip = str(attr[attrName])
  1041.                 elif attrName == 'broadcast':
  1042.                     broadcast = str(attr[attrName])
  1043.                 elif attrName == 'netmask':
  1044.                     netmask = str(attr[attrName])
  1045.                 elif attrName == 'options':
  1046.                     dhcp_options = str(attr[attrName])
  1047.  
  1048.             if ip != 'dhcp' and ip != None:
  1049.                 dhcp = False
  1050.  
  1051.         if not dhcp:
  1052.             if not GLIUtility.is_ip(ip):
  1053.                 raise GLIException("NetworkInterfacesError", 'fatal', 'add_network_interface',  "The ip address you specified for " + device + " is not valid!")
  1054.             if not GLIUtility.is_ip(broadcast):
  1055.                 raise GLIException("NetworkInterfacesError", 'fatal', 'add_network_interface',  "The broadcast address you specified for " + device + " is not valid!")
  1056.             if not GLIUtility.is_ip(netmask):
  1057.                 raise GLIException("NetworkInterfacesError", 'fatal', 'add_network_interface',  "The netmask address you specified for " + device + " is not valid!")
  1058.             options = (ip, broadcast, netmask)
  1059.         else:
  1060.             options = ('dhcp', dhcp_options, None)
  1061.  
  1062.         self._network_interfaces[device] = options
  1063.  
  1064.     ##
  1065.     # This method sets the network interfaces diction to network_interfaces.
  1066.     # This method uses the function add_network_interfaces to do all of the real work.
  1067.     # @param network_interfaces   a dict with all the networking info.  see add_ for specification.
  1068.     def set_network_interfaces(self, network_interfaces):
  1069.         # Check type
  1070.         if type(network_interfaces) != dict:
  1071.             raise GLIException("NetworkInterfacesError", 'fatal', 'set_network_interfaces',  "Must be a dictionary!")
  1072.  
  1073.         self._network_interfaces = {}
  1074.         for device in network_interfaces:
  1075.             self.add_network_interface(None, device, network_interfaces[device])
  1076.  
  1077.     ##
  1078.     # Returns network_interfaces
  1079.     def get_network_interfaces(self):
  1080.         return self._network_interfaces
  1081.         
  1082.     ##
  1083.     # Serialize Network Interfaces
  1084.     def serialize_network_interfaces(self):
  1085.         if self.get_network_interfaces() != {}:
  1086.             self.xmldoc += "<network-interfaces>"
  1087.             interfaces = self.get_network_interfaces()
  1088.             for iface in interfaces:
  1089.                 if interfaces[iface][0] == 'dhcp':
  1090.                     attrs = "ip=\"dhcp\""
  1091.                     if interfaces[iface][1] != None:
  1092.                         dhcp_options = "options=\"%s\"" % interfaces[iface][1]
  1093.                         attrs = attrs + " " + dhcp_options
  1094.                     self.xmldoc += "<device %s>%s</device>" % (attrs, iface)
  1095.                 else:
  1096.                     self.xmldoc += "<device ip=\"%s\" broadcast=\"%s\" netmask=\"%s\">%s</device>" % (interfaces[iface][0], interfaces[iface][1], interfaces[iface][2], iface)
  1097.             self.xmldoc += "</network-interfaces>"
  1098.  
  1099.     ############################################################################
  1100.     #### Network Mounts
  1101.  
  1102.     ##
  1103.     # FIXME: agaffney: Brief description of function
  1104.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1105.     # @param unused Parameter description
  1106.     # @param attr Parameter description
  1107.     def add_netmount(self, xml_path, unused, attr):
  1108.         netmount_entry = {'export': '', 'host': '', 'mountopts': '', 'mountpoint': '', 'type': ''}
  1109.         if type(attr) == tuple:
  1110.             netmount_entry['export'] = attr[0]
  1111.             netmount_entry['host'] = attr[1]
  1112.             netmount_entry['mountopts'] = attr[2]
  1113.             netmount_entry['mountpoint'] = attr[3]
  1114.             netmount_entry['type'] = attr[4]
  1115.         else:
  1116.             if "export" in attr.getNames():
  1117.                 for attrName in attr.getNames():
  1118.                     netmount_entry[attrName] = str(attr.getValue(attrName))
  1119.         self._network_mounts.append(netmount_entry)
  1120.  
  1121.     ##
  1122.     # Sets Network Mounts given a netmounts found probably in the config file.  Not sure if used.
  1123.     # @param netmounts Parameter description
  1124.     def set_network_mounts(self, netmounts):
  1125.         self._network_mounts = netmounts
  1126.  
  1127.     ##
  1128.     # Returns the network mounts.
  1129.     def get_network_mounts(self):
  1130.         return self._network_mounts
  1131.         
  1132.     ##
  1133.     # Serializes network mounts
  1134.     def serialize_network_mounts(self):
  1135.         if self.get_network_mounts() != {}:
  1136.             netmounts = self.get_network_mounts()
  1137.             self.xmldoc += "<network-mounts>"
  1138.             for mount in netmounts:
  1139.                 self.xmldoc += "<netmount host=\"%s\" export=\"%s\" type=\"%s\" mountpoint=\"%s\" mountopts=\"%s\" />" % (mount['host'], mount['export'], mount['type'], mount['mountpoint'], mount['mountopts'])
  1140.             self.xmldoc += "</network-mounts>"
  1141.         
  1142.     ############################################################################
  1143.     #### NIS Domain Name
  1144.  
  1145.     ##
  1146.     # nisdomainname is a string containing the NIS domainname for the new system.
  1147.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1148.     # @param nisdomainname  the name. is a string.
  1149.     # @param xml_attr not used here
  1150.     def set_nisdomainname(self, xml_path, nisdomainname, xml_attr):
  1151.         # Check type
  1152.         if type(nisdomainname) != str:
  1153.             raise GLIException("NISDomainnameError", 'fatal', 'set_nisdomainname',  "Must be a string!")
  1154.             
  1155.         self._nisdomainname = nisdomainname
  1156.     
  1157.     ##
  1158.     # Returns nisdomainname
  1159.     def get_nisdomainname(self):
  1160.         return self._nisdomainname
  1161.  
  1162.     ############################################################################
  1163.     #### Partitioning
  1164.  
  1165.     ##
  1166.     # FIXME: agaffney
  1167.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1168.     # @param unused Parameter description
  1169.     # @param attr Parameter description
  1170.     def add_partitions_device(self, xml_path, unused, attr):
  1171.         devnode = None
  1172.         if type(attr) == tuple:
  1173.             devnode = attr[0]
  1174.             disklabel = attr[1]
  1175.         else:
  1176.             if "devnode" in attr.getNames():
  1177.                 devnode = str(attr.getValue("devnode"))
  1178.                 if "disklabel" in attr.getNames():
  1179.                     disklabel = str(attr.getValue("disklabel"))
  1180.                 else:
  1181.                     disklabel = ""
  1182.         self._partition_current_device = devnode
  1183.         self._partition_tables[devnode] = GLIStorageDevice.Device(devnode)
  1184.         self._partition_tables[devnode].set_disklabel(disklabel)
  1185.         # Add code to import self._temp_partition_table into the Device object
  1186.         self._partition_tables[devnode].set_partitions_from_install_profile_structure(self._temp_partition_table)
  1187.         self._temp_partition_table = {}
  1188.             
  1189.  
  1190.     ##
  1191.     # FIXME: agaffney
  1192.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1193.     # @param unused Parameter description
  1194.     # @param attr Parameter description
  1195.     def add_partitions_device_partition(self, xml_path, unused, attr):
  1196.         part_entry = {'end': 0, 'format': None, 'mb': 0, 'minor': 0, 'mountopts': '', 'mountpoint': '', 'origminor': '', 'start': 0, 'type': ''}
  1197. #        if type(attr) == tuple:
  1198. #            part_entry['end'] = attr[0]
  1199. #            part_entry['format'] = attr[1]
  1200. #            part_entry['mb'] = attr[2]
  1201. #            part_entry['minor'] = attr[3]
  1202. #            part_entry['mountopts'] = attr[4]
  1203. #            part_entry['mountpoint'] = attr[5]
  1204. #            part_entry['origminor'] = attr[6]
  1205. #            part_entry['start'] = attr[7]
  1206. #            part_entry['type'] = attr[8]
  1207. #        else:
  1208.         if "minor" in attr.getNames():
  1209.             for attrName in attr.getNames():
  1210.                 part_entry[attrName] = str(attr.getValue(attrName))
  1211.         if type(part_entry['format']) == str: part_entry['format'] = GLIUtility.strtobool(part_entry['format'])
  1212.         if type(part_entry['resized']) == str: part_entry['resized'] = GLIUtility.strtobool(part_entry['resized'])
  1213. #        if GLIUtility.is_numeric(part_entry['end']): part_entry['end'] = long(part_entry['end'])
  1214. #        if GLIUtility.is_numeric(part_entry['start']): part_entry['start'] = long(part_entry['start'])
  1215.         if GLIUtility.is_numeric(part_entry['mb']): part_entry['mb'] = long(part_entry['mb'])
  1216.         if GLIUtility.is_numeric(part_entry['minor']):
  1217.             if part_entry['type'] == "free":
  1218.                 part_entry['minor'] = float(part_entry['minor'])
  1219.             else:
  1220.                 part_entry['minor'] = int(part_entry['minor'])
  1221.         if GLIUtility.is_numeric(part_entry['origminor']): part_entry['origminor'] = int(part_entry['origminor'])
  1222.         self._temp_partition_table[part_entry['minor']] = part_entry
  1223.  
  1224.     ############################################################################
  1225.     #### Partition Tables
  1226.     
  1227.     ##
  1228.     # Sets the partition Tables
  1229.     # @param partition_tables   multilevel dictionary described below.
  1230.     def set_partition_tables(self, partition_tables):
  1231.         """
  1232.         Sets the partition tables.  A partition is a multi level dictionary in the following format:
  1233.         { <device (local)>: <partition table>, <device (nfs)>: <mount point> }
  1234.         
  1235.         <device (local)> is a string containing the path to the local file. (ie. '/dev/hda')
  1236.         <device (nfs)> is a string containing the ip address of the nfs mount. (ie. '192.168.1.2')
  1237.         
  1238.         <partition table> is a dictionary in the following format:
  1239.             { <minor>: { 'mb': <size in mb>, 'type': <type>, 'mountpoint': <mount point>, 'start': <start cylinder>,
  1240.                          'end': <end cylinder>, 'mountopts': <mount options>, 'format': <format> } }
  1241.         
  1242.         ie. partition_tables['/dev/hda'][1] would return { 'mb': 0, 'type': 'ext3', 'mountpoint': '/boot', 'start': 12345,
  1243.                                    'end': 34567, 'mountopts': 'auto', format: 'False' }
  1244.  
  1245.         Types are as follows:
  1246.         string: <device>, <mount point>, <mount options>, <type>
  1247.         integer: <minor>, <size in mb>, <start cylinder>, <end cylinder>
  1248.         boolean: <format>
  1249.         
  1250.         Current <type> options include:
  1251.         ext2, ext3, reiserfs, xfs, jfs, linux-swap, extended, others?
  1252.         
  1253.         There will be a method in the partitioning code to make sure that the current parition_tables can actually be implemented.
  1254.         Should we call that function to test the culpability of our potential partitioning scheme?
  1255.         Should we create a method in the Controller to take raw variables and put them in the proper structure?
  1256.         Are all filesystems supported by all arches?
  1257.         """
  1258.         
  1259.         # All the sanity checks are being commented out until I can fix them for the GLIStorageDevice stuff
  1260.         """
  1261.         if type(partition_tables) != dict:
  1262.             raise GLIException("PartitionTableError", 'fatal', 'set_partition_tables',  "Invalid data type! partition_tables is a dict...")
  1263.         
  1264.         for device in partition_tables:
  1265.         
  1266.             # If the device is a valid local device...
  1267.             if GLIUtility.is_device(device):
  1268.             
  1269.                 # We should check to make sure device is in /proc/partitions
  1270.                 # If it is in /proc/partitions, it is a partitionable device
  1271.             
  1272.                 # ... then loop through each minor to check data
  1273.                 for minor in partition_tables[device]:
  1274.  
  1275.                     # Make sure that the <minor> is an integer or can be converted to one
  1276.                     try:
  1277.                         int(minor)
  1278.                     except:
  1279.                         raise GLIException("ParitionTableError", 'fatal', 'set_partition_tables',  "The minor you specified (" + minor + ") is not an integer!")
  1280.                     
  1281.                     # Make sure that a minor number is valid
  1282.                     if minor < 1:
  1283.                         raise GLIException("ParitionTableError", 'fatal', 'set_partition_tables',  "The minor you specified (" + minor + ") is not a valid minor!")
  1284.                 
  1285.                     # Make sure that <size>, <type> and <mount point> are all set
  1286.                     #if len(partition_tables[device][minor]) != 3:
  1287.                     #    raise GLIException("ParitionTableError", 'fatal', 'set_partition_tables',  "The number of attributes for minor " + minor + " is incorrect!")
  1288.                     #
  1289.                     # Make sure that the <size> is an integer or can be converted to one
  1290.                     #try:
  1291.                     #    int(partition_tables[device][minor][0])
  1292.                     #except:
  1293.                     #    raise GLIException("ParitionTableError", 'fatal', 'set_partition_tables',  "The size you specified (" + partition_tables[device][minor][0] + ") is not an integer!")
  1294.  
  1295.             # Else, if the device is a valid remote device (hostname or ip)
  1296.             elif GLIUtility.is_ip(device) or GLIUtility.is_hostname(device):
  1297.             
  1298.                 pass
  1299.                 # Make sure that only the mount point is set
  1300.             #    if type(partition_tables[device]) != str:
  1301.             #        raise GLIException("ParitionTableError", 'fatal', 'set_partition_tables',  "Invalid mount point for nfs mount (device: " + device + ")!")
  1302.  
  1303.             # If the device is not a local or remote device, then it is invalid
  1304.             else:
  1305.                 raise GLIException("PartitionTableError", 'fatal', 'set_partition_tables',  "The device you specified (" + device + ") is not valid!")
  1306.         """
  1307.  
  1308.         # If all the tests clear, then set the variable
  1309.         self._partition_tables = partition_tables
  1310.  
  1311.     ##
  1312.     # Returns partition_tables
  1313.     def get_partition_tables(self):
  1314.         return self._partition_tables
  1315.     
  1316.     ##
  1317.     # Serializes partition tables
  1318.     def serialize_partition_tables(self):
  1319.         if self.get_partition_tables() != {}:
  1320.             partitions = self.get_partition_tables()
  1321.             self.xmldoc += "<partitions>";
  1322.             for device in partitions.keys():
  1323.                 self.xmldoc += "<device devnode=\"%s\" disklabel=\"%s\">" % (device, partitions[device].get_disklabel())
  1324.                 ips = partitions[device].get_install_profile_structure()
  1325.                 for minor in ips:
  1326.                     part = ips[minor]
  1327.                     self.xmldoc += "<partition minor=\"%s\" origminor=\"%s\" mb=\"%s\" type=\"%s\" mountpoint=\"%s\" mountopts=\"%s\" format=\"%s\" mkfsopts=\"%s\" resized=\"%s\" />" % (str(minor), str(part['origminor']), str(part['mb']), str(part['type']), str(part['mountpoint']), str(part['mountopts']), str(part['format']), str(part['mkfsopts']), str(part['resized']))
  1328.                 self.xmldoc += "</device>"
  1329.             self.xmldoc += "</partitions>"
  1330.  
  1331.     ############################################################################
  1332.     #### Portage Snapshot URI
  1333.  
  1334.     ##
  1335.     # portage_tree_snapshot_uri is a string defining the path to a portage tree 
  1336.     # snapshot. (ie. 'file:///mnt/cdrom/snapshots/portage-*.tar.bz2')
  1337.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1338.     # @param portage_tree_snapshot_uri  URI of the portage tree shapshot location
  1339.     # @param xml_attr         not used here
  1340.     def set_portage_tree_snapshot_uri(self, xml_path, portage_tree_snapshot_uri, xml_attr):
  1341.         # Check type
  1342.         if type(portage_tree_snapshot_uri) != str:
  1343.             raise GLIException("PortageTreeSnapshotURIError", 'fatal', 'set_portage_tree_snapshot_uri',  "Must be a string!")
  1344.  
  1345.         self._portage_tree_snapshot_uri = portage_tree_snapshot_uri
  1346.  
  1347.     ##
  1348.     # Returns portage_tree_snapshot_uri
  1349.     def get_portage_tree_snapshot_uri(self):
  1350.         return self._portage_tree_snapshot_uri
  1351.         
  1352.     ############################################################################
  1353.     #### Portage Tree Sync Type
  1354.  
  1355.     ##
  1356.     # Sets the sync type to be used by portage
  1357.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1358.     # @param portage_tree_sync  string with sync type
  1359.     # @param xml_attr         not used here
  1360.     def set_portage_tree_sync_type(self, xml_path, portage_tree_sync, xml_attr):
  1361.         # Check type
  1362.         if type(portage_tree_sync) != str:
  1363.             raise GLIException("PortageTreeSyncError", 'fatal', 'set_portage_tree_sync_type',  "Must be a string!")
  1364.  
  1365.         if string.lower(portage_tree_sync) not in ('sync', 'webrsync', 'custom', 'snapshot', 'none'):
  1366.             raise GLIException("PortageTreeSyncError", 'fatal', 'set_portage_tree_sync_type',  "Invalid Input!")
  1367.  
  1368.         self._portage_tree_sync_type = string.lower(portage_tree_sync)
  1369.  
  1370.     ##
  1371.     # Returns portage_tree_sync
  1372.     def get_portage_tree_sync_type(self):
  1373.         return self._portage_tree_sync_type    
  1374.  
  1375.     ############################################################################
  1376.     #### Post-Install Script URI
  1377.     
  1378.     ##
  1379.     # Sets the URI for the post install script
  1380.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1381.     # @param post_install_script_uri the URI
  1382.     # @param xml_attr Parameter description
  1383.     def set_post_install_script_uri(self, xml_path, post_install_script_uri, xml_attr):
  1384.         self._post_install_script_uri = post_install_script_uri
  1385.     
  1386.     ##
  1387.     # Returns the URI for the post install script
  1388.     def get_post_install_script_uri(self):
  1389.         return self._post_install_script_uri
  1390.  
  1391.     ############################################################################
  1392.     #### /etc/rc.conf Configuration
  1393.  
  1394.     ##
  1395.     # Adds a variable set for the new system rc.conf
  1396.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1397.     # @param data         a string that is the value of the variable name.
  1398.     # @param attr         an xml attribute that contains the name of the variable
  1399.     def rc_conf_add_var(self, xml_path, data, attr):
  1400.         if 'name' not in attr.keys():
  1401.             raise GLIException("RCConfError", 'fatal', 'rc_conf_add_var',  "Every value needs to have a variable name!")
  1402.  
  1403.         varName = attr['name']
  1404.         if not "rc.conf" in self._etc_files:
  1405.             self._etc_files['rc.conf'] = {}
  1406.         self._etc_files[str(varName)] = str(data)
  1407.  
  1408.     ##
  1409.     # rc_conf is a dictionary that will be set to _rc_conf
  1410.     # There is no checking that needs to be done, so please sure sure that the rc_conf dictionary
  1411.     # that is passed in is valid.Brief description of function
  1412.     # @param rc_conf     dictionary in the format specified above.
  1413.     def set_rc_conf(self, rc_conf):
  1414.         self._etc_files['rc.conf'] = rc_conf
  1415.  
  1416.     ##
  1417.     # Return a dictionary of the make.conf
  1418.     def get_rc_conf(self):
  1419.         if "rc.conf" in self._etc_files:
  1420.             return self._etc_files['rc.conf']
  1421.         else:
  1422.             return {}
  1423.         
  1424.     ##
  1425.     # Serializes rc.conf (no longer used)
  1426.     def serialize_rc_conf(self):
  1427.         if self.get_rc_conf() != {}:
  1428.             self.xmldoc += "<rc-conf>"
  1429.  
  1430.             rc_conf = self.get_rc_conf()
  1431.             for var in rc_conf:
  1432.                 self.xmldoc += "<variable name=\"%s\">%s</variable>" % (var, rc_conf[var])
  1433.  
  1434.             self.xmldoc += "</rc-conf>"
  1435.  
  1436.     ############################################################################
  1437.     #### Root Password Hash
  1438.  
  1439.     ##
  1440.     # root_pass_hash is a string containing an md5 password hash to be assinged as the password for the root user.
  1441.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1442.     # @param root_pass_hash Parameter description
  1443.     # @param xml_attr Parameter description
  1444.     def set_root_pass_hash(self, xml_path, root_pass_hash, xml_attr):
  1445.         # Check type
  1446.         if type(root_pass_hash) != str:
  1447.             raise GLIException("RootPassHashError", 'fatal', 'set_root_pass_hash',  "Must be a string!")
  1448.         self._root_pass_hash = root_pass_hash
  1449.  
  1450.     ##
  1451.     # Returns root_pass_hash
  1452.     def get_root_pass_hash(self):
  1453.         return self._root_pass_hash
  1454.  
  1455.     ############################################################################
  1456.     #### RSYNC Proxy
  1457.  
  1458.     ##
  1459.     # RSYNC proxy is a uri containing a proxy if needed for rsync traffic. (ie. 'rsync://myhost.mydomain:myport')
  1460.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1461.     # @param rsync_proxy     the proxy address
  1462.     # @param xml_attr not used here
  1463.     def set_rsync_proxy(self, xml_path, rsync_proxy, xml_attr):
  1464.         # Check type
  1465.         if rsync_proxy and not GLIUtility.is_uri(rsync_proxy):
  1466.             raise GLIException("RSYNCProxyError", 'fatal', 'set_rsync_proxy',  "Must be a uri!")
  1467.         self._rsync_proxy = rsync_proxy
  1468.  
  1469.     ##
  1470.     # Returns RSYNC proxy
  1471.     def get_rsync_proxy(self):
  1472.         return self._rsync_proxy
  1473.  
  1474.     ############################################################################
  1475.     #### Services
  1476.     
  1477.     ##
  1478.     # Set the services to be started on bootup. Services should be
  1479.     # seperated by ','. WARNING: This used to be ' ' instead!
  1480.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1481.     # @param services         comma-separated list of services
  1482.     # @param xml_attr Parameter description
  1483.     def set_services(self, xml_path, services, xml_attr):
  1484.         if type(services) == str:
  1485.             services = services.split(',')
  1486.         else:
  1487.             raise GLIException("ServicesError", 'fatal', 'set_services',  "Invalid input!")
  1488.  
  1489.         for service in services:
  1490.             if not GLIUtility.is_realstring(service):
  1491.                 raise GLIException("ServicesError", 'fatal', 'set_services',  service + " must be a valid string!")
  1492.         self._services = services
  1493.  
  1494.     ##
  1495.     # This returns a list of the packages:
  1496.     def get_services(self):
  1497.         return self._services
  1498.         
  1499.     ##
  1500.     # Serializes services
  1501.     def serialize_services(self):
  1502.         if self.get_services() != ():
  1503.             self.xmldoc += "<services>"
  1504.             self.xmldoc += string.join(self.get_services(), ',')
  1505.             self.xmldoc += "</services>"
  1506.  
  1507.     ############################################################################
  1508.     #### Stage Tarball URI
  1509.  
  1510.     ##
  1511.     # stage_tarball_uri is a string that is the full path to the tarball you 
  1512.     # wish to use. (ie. 'file:///path/to/mytarball.tar.bz2')
  1513.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1514.     # @param stage_tarball_uri   string of URI for stage tarball location.
  1515.     # @param xml_attr   not used here.
  1516.     def set_stage_tarball_uri(self, xml_path, stage_tarball_uri, xml_attr):
  1517.         # Check type
  1518.         if type(stage_tarball_uri) != str:
  1519.             raise GLIException("StageTarballURIError", 'fatal', 'set_stage_tarball_uri',  "Must be a string!")
  1520.  
  1521.         # Check validity (now done in the FE)
  1522.         #if not stage_tarball_uri:
  1523.         #    raise GLIException("CustomStage3TarballURIError", 'fatal', 'set_stage_tarball_uri',  "Empty URI!")
  1524.         
  1525.         self._stage_tarball_uri = stage_tarball_uri
  1526.  
  1527.     ##
  1528.     # Returns stage_tarball_uri
  1529.     def get_stage_tarball_uri(self):
  1530.         return self._stage_tarball_uri
  1531.  
  1532.     ############################################################################
  1533.     #### Timezone
  1534.  
  1535.     ##
  1536.     # time_zone is a string defining the time zone to use.  
  1537.     # Time zones are found in /usr/share/zoneinfo/.  Syntax is 'UTC' or 'US/Eastern'.
  1538.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1539.     # @param time_zone         string of the intended timezone
  1540.     # @param xml_attr         not used here.
  1541.     def set_time_zone(self, xml_path, time_zone, xml_attr):
  1542.         # Check type
  1543.         if type(time_zone) != str:
  1544.             raise GLIException("TimeZoneError", 'fatal', 'set_time_zone',  "Must be a string!")
  1545.         self._time_zone = time_zone
  1546.  
  1547.     ##
  1548.     # Returns time_zone
  1549.     def get_time_zone(self):
  1550.         return self._time_zone
  1551.  
  1552.     ############################################################################
  1553.     #### Users
  1554.     
  1555.     ##
  1556.     # Adds a user to the list of users
  1557.     # @param xml_path Used internally by the XML parser. Should be None when calling directly
  1558.     # @param username          name of user to be added
  1559.     # @param attr=None      parameters for the user.
  1560.     def add_user(self, xml_path, username, attr=None):
  1561.         """
  1562.         This will take a username (that is a string) and a set of attributes and it will verify everything is valid
  1563.         and convert it into a 7-tuple set. Then it adds this tuple into the users list.
  1564.         username and hash are manditory. All other attributes are optional. Or this method will
  1565.         take a 7-tuple set, verify it's correctness and then append it to the _users list.
  1566.         All items are strings except <uid>, which is an integer, and groups, which is a tuple. 
  1567.  
  1568.         The finished tuples look like this:
  1569.         ( <user name>, <password hash>, (<tuple of groups>), <shell>, <home directory>, <user id>, <user comment> )
  1570.  
  1571.         """
  1572.         hash = ''
  1573.         shell = None
  1574.         groups = None
  1575.         shell = None
  1576.         homedir = None
  1577.         uid = None
  1578.         comment = None
  1579.  
  1580.         if type(username) == tuple:
  1581.             if len(username) != 7:
  1582.                 raise GLIException("UserError", 'fatal', 'add_user',  "Wrong format for user tuple!")
  1583.  
  1584.             username_tmp = username[0]
  1585.             hash = username[1]
  1586.             groups = username[2]
  1587.             shell = username[3]
  1588.             homedir = username[4]
  1589.             uid = username[5]
  1590.             comment = username[6]
  1591.             username = username_tmp
  1592.  
  1593.             if type(groups) != tuple:
  1594.                 if groups != None:
  1595.                     groups = tuple(groups.split(','))
  1596.         else:
  1597.             for attrName in attr.keys():
  1598.                 if attrName == 'groups':
  1599.                     groups = tuple(str(attr[attrName]).split(','))
  1600.                 elif attrName == 'shell':
  1601.                     shell = str(attr[attrName])
  1602.                 elif attrName == 'hash':
  1603.                     hash = str(attr[attrName])
  1604.                 elif attrName == 'homedir':
  1605.                     homedir = str(attr[attrName])
  1606.                 elif attrName == 'uid':
  1607.                     if attr[attrName]:
  1608.                         uid = int(attr[attrName])
  1609.                 elif attrName == 'comment':
  1610.                     comment = str(attr[attrName])
  1611.  
  1612.         allowable_nonalphnum_characters = '_-'
  1613.  
  1614.         if not GLIUtility.is_realstring(username):
  1615.             raise GLIException("UserError", 'fatal', 'add_user',  "username must be a non-empty string")
  1616.  
  1617.         if username[0] not in (string.lowercase + string.uppercase):
  1618.             raise GLIException("UsersError", 'fatal', 'add_user',  "A username must start with a letter!")
  1619.  
  1620.         for x in username:
  1621.             if x not in (string.lowercase + string.uppercase + string.digits + allowable_nonalphnum_characters):
  1622.                 raise GLIException("UsersError", 'fatal', 'add_user', "A username must contain only letters, numbers, or these symbols: " + allowable_nonalphnum_characters)
  1623.  
  1624.         for user in self._users:
  1625.             if username == user[0]:
  1626.                 raise GLIException("UserError", 'fatal', 'add_user',  "This username already exists!")
  1627.  
  1628.         if (hash == None) or (hash == ''):
  1629.             raise GLIException("UserError", 'fatal', 'add_user',  "A password hash must be given for every user!")
  1630.  
  1631.         self._users.append((username,hash,groups,shell,homedir,uid,comment))
  1632.  
  1633.     ##
  1634.     # Remove "username" from the _users list.
  1635.     # @param username    name of user to be removed
  1636.     def remove_user(self, username):
  1637.         for user in self._users:
  1638.             if username == user[0]:
  1639.                 self._users.remove(user)
  1640.                 break
  1641.  
  1642.     ##
  1643.     # users is a tuple(user) of tuple's. This sets _users to this set of tuples.
  1644.     # @param users   a tuple(user) of tuple's.
  1645.     def set_users(self, users):
  1646.         self._users = []
  1647.         if users != None:
  1648.             for user in users:
  1649.                 self._users.append(user)
  1650.  
  1651.     ##
  1652.     # Returns users
  1653.     def get_users(self):
  1654.         return self._users
  1655.         
  1656.     ##
  1657.     # Serializes users
  1658.     def serialize_users(self):
  1659.         if self.get_users() != []:
  1660.             self.xmldoc += "<users>"
  1661.             users = self.get_users()
  1662.             for user in users:
  1663.                 attrstr = ""
  1664.                 username = user[0]
  1665.  
  1666.                 if user[1] != None:
  1667.                     attrstr += "hash=\"%s\" " % user[1]
  1668.                 if user[2] != None:
  1669.                     attrstr += "groups=\"%s\" " % string.join(user[2],',')
  1670.                 if user[3] != None:
  1671.                     attrstr += "shell=\"%s\" " % user[3]
  1672.                 if user[4] != None:
  1673.                     attrstr += "homedir=\"%s\" " % user[4]
  1674.                 if user[5] != None:
  1675.                     attrstr += "uid=\"%s\" " % user[5]
  1676.                 if user[6] != None:
  1677.                     attrstr += "comment=\"%s\" " % user[6]
  1678.  
  1679.                 self.xmldoc += "<user %s>%s</user>" % (string.strip(attrstr), username)
  1680.             self.xmldoc += "</users>"
  1681.